<?php
/**
 * 操作条件
 * 
 * Copyright (c) 2014~2019 https://desion.net All rights reserved.
 * Author: 阿林 QQ:120867629 <linhong00@gmail.com>
 */
declare (strict_types = 1);
namespace dsphp\model;

trait Where
{
    // 读取字段
    private $_optionsDefault = [
        'field'     => [],
        'table'     => '',
        'alias'     => '',
        'partition' => '',
        'join'      => [],
        'group'     => [],
        'having'    => '',
        'limit'     => '',
        'page'      => '',
        'order'     => [],
        'force'     => [],
        'comment'   => '',
        'lock'      => false,
        'where'     => [],
        'procedure' => false,
        'master'    => false,
        'replace'   => false,
        'duplicate' => [],
        'data'      => '',
    ];
    // 读取字段
    private $_options = [];
    // 返回sql
    private $_returnSql = false;
    // 查询规则
    protected $whereOption = ['=', '<>', '>', '>=', '<', '<=', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN', 'NOT LIKE', 'LIKE', 'FIND'];
    // 条件逻辑
    protected $whereLogic = ['AND', 'OR'];

    // 还原操作参数
    private function _initOptions()
    {
        $this->_options   = $this->_optionsDefault;
        $this->_returnSql = false;
    }

    /**
     * 设置读取字段
     *
     * @param string $field  字段
     * @param string $alias  明白
     * @return this
     */
    public function field($field, string $alias = '') 
    {
        if (is_array($field)) {
            $this->_options['field'] = array_merge($this->_options['field'], $field);
            return $this;
        }

        if ($alias) {
            $this->_options['field'][$alias] = $field;
        } else {
            $this->_options['field'][] = $field;
        }

        return $this;
    }

    /**
     * 设置读取别名
     *
     * @param string $name  别名
     * @return this
     */
    public function alias(string $name) 
    {
        if ($name) {
            $this->_options['alias'] = $name;
        }

        return $this;
    }

    /**
     * 设置读取分区
     *
     * @param string $name  分区
     * @return this
     */
    public function partition(string $name) 
    {
        if ($name) {
            $this->_options['partition'] = $name;
        }

        return $this;
    }

    /**
     * 设置连表
     *
     * @param mixed   $table  表
     * @param string  $where  条件
     * @param string  $type   默认`INNER`、`LEFT`、`RIGHT`、`FULL`  
     * @return this
     */
    public function join($table, string $where, string $type = 'INNER')
    {
        $name = '';
        if (is_array($table)) {
            foreach ($table as $key => $val) {
                $name = "{$key} {$val}";
            }
        } else {
            $name = $this->tablePrefix . $table;
        }

        $this->_options['join'][] = [$name, $where, $type];
        return $this;
    }

    /**
     * 设置having
     *
     * @param string $having  
     * @return this
     */
    public function having(string $having = '')
    {
        $this->_options['having'] = $having;
        return $this;
    }

    /**
     * 设置group
     *
     * @param mixed $group  分组 
     * @return this
     */
    public function group($group = '')
    {
        if (is_array($group)) {
            $this->_options['group'] = $group;
        } elseif (is_string($group)) {
            $this->_options['group'][] = $group;
        }
        
        return $this;
    }

    /**
     * 设置limit
     *
     * @param integer $nums  页码  
     * @param integer $limit 条数  
     * @return this
     */
    public function page(int $nums = 1, int $limit = self::PAGE_LIMIT) 
    {
        if ($limit === 0) {
            $this->_options['limit'] = '';
            return $this;
        }

        $nums  = $nums < 1 ? 1 : $nums;
        $start = $limit * ($nums - 1);

        $this->_options['limit'] = "{$start},{$limit}";
        return $this;
    }

    /**
     * 设置排序
     *
     * @param  string $field  
     * @param  string $order
     * @return this
     */
    public function order(string $field, string $sort = 'DESC')
    {
        if ($field) {
            $this->_options['order'][$field] = $sort;
        }

        return $this;
    }

    /**
     * 设置强制索引
     *
     * @param string $field
     * @return this
     */
    public function force(string $field)
    {
        if ($field) {
            $this->_options['force'][] = $field;
        }

        return $this;
    }

    /**
     * 设置注销析
     *
     * @param string $text  说明
     * @return this
     */
    public function comment(string $text)
    {
        if ($text) {
            $this->_options['comment'] = $text;
        }

        return $this;
    }

    /**
     * 设置锁定
     *
     * @param boolean $text  说明
     * @return this
     */
    public function lock(bool $status)
    {
        if ($status) {
            $this->_options['lock'] = $status;
        }

        return $this;
    }

    /**
     * 设置查询从主
     *
     * @param boolean $status  是否
     * @return this
     */
    public function master(bool $status = true)
    {
        $this->_options['master'] = $status;
        return $this;
    }

    /**
     * 设置存储过程
     *
     * @param boolean $status  是否
     * @return this
     */
    public function procedure(bool $status = true)
    {
        $this->_options['procedure'] = $status;
        return $this;
    }  

    /**
     * 设置返回sql
     *
     * @param boolean $status  是否
     * @return this
     */
    public function sql(bool $status = true)
    {
        $this->_returnSql = $status;
        return $this;
    } 
    
    /**
     * 设置插入替换
     *
     * @param boolean $status  是否
     * @return this
     */
    public function replace(bool $status = false)
    {
        $this->_options['replace'] = $status;
        return $this;
    }  
    
    /**
     * 设置插入唯一更新
     *
     * @param array $sql  更新sql
     * @return this
     */
    public function duplicate(array $sql)
    {
        if ($sql) {
            $this->_options['duplicate'] = $sql;
        }
        
        return $this;
    }   
    
    /**
     * 设置插入数据
     *
     * @param mixed $field  字段名称或者全部数据
     * @param mixed $value  值
     * @return this
     */
    public function data($field, $value = false)
    {
        if (is_string($field)) {
            if (is_string($value) || is_numeric($value)) {
                $this->_options['data'][$field] = $value;
            }

            return $this;
        }

        if (! is_array($field)) {
            return $this;
        }

        if ($value === false) {
            $field = array_merge($this->_options['data'], $field);
        } 

        $this->_options['data'] = $field;
        return $this;
    } 

    /**
     * 设置条件
     *
     * @param mixed  $field   字段或条件数组(数组直接合并，后面参数失效)
     * @param mixed  $value   字段时有效，字段值
     * @param string $option  运算标识
     * @return this
     */
    public function where($field, string $value = '', string $option = '=')
    {
        if (is_array($field)) {
            $field = $this->_checkMap($field, false);
            $this->_options['where'] = array_merge($this->_options['where'], $field);
        } else {
            $this->_options['where'][] = $this->_checkWhereList($field, $value, $option, 'AND');
        }

        return $this;
    }

    /**
     * 设置OR条件 
     *
     * @param mixed  $field   字段
     * @param mixed  $value   字段时有效，字段值
     * @param string $option  运算标识
     * @return this
     */
    public function whereOr(string $field, $value = '', string $option = '=')
    {
        return $this->_setWhere($field, $value, $option, 'OR');
    }

    /**
     * 条件
     *
     * @param array $list    条件
     * @param string $logic  逻辑
     * @return this
     */
    public function whereMap(array $list, string $logic = 'AND')
    {
        $this->_options['where']['map'][] = $this->_checkMap([$logic, $list]);

        return $this;
    }

    /**
     * 检测输入填入
     *
     * @param array $list     
     * @param boolean $isMap  是否
     */
    private function _checkMap(array $list, $isMap = true):array
    {
        if ($isMap) {
            $map  = [$list];
        } else {
            if (isset($list['map'])) {
                $map = $list['map'];
                unset($list['map']);
            } else {
                $map = [];
            }

            foreach ($list as $key => $info) {
                $list[$key] = $this->_checkWhereList($info);
            }
        }

        foreach ($map as $key => $info) {
            $map[$key][0] = $this->_checkWhereLogic($info[0]);
            
            foreach ($info[1] as $k => $v) {
                $map[$key][1][$k] = $this->_checkWhereList($v);
            }
        }

        if ($isMap) {
            return $map[0];
        }

        $list['map'] = $map;
        return $list;
    }

    // 简称条件
    private function _checkWhereList($field, $value = '', string $option = '', string $logic = '') : array
    {
        if (is_array($field)) {
            list($field, $value, $option, $logic) = $field;
        }
        
        $value  = $this->_checkWhereValue($value);
        $option = $this->_checkWhereOption($option);
        $logic  = $this->_checkWhereLogic($logic);

        return [$field, $value, $option, $logic];
    }

    /**
     * 检测条件的值
     *
     * @param string|integer $value  条件的值
     */
    private function _checkWhereValue($value):string
    {
        if (! is_string($value) && ! is_integer($value) && ! is_array($value)) {
            return '';
        }

        if (is_array($value)) {
            $value = implode(',', $value);
        }

        return (string)$value;
    }

    /**
     * 检测条件的操作类型
     *
     * @param string $option  条件操作
     * @return string
     */
    private function _checkWhereOption(string $option):string
    {
        $option = strtoupper($option);
        if (in_array($option, $this->whereOption)) {
            return $option;
        }

        switch ($option)
        {
            case 'FIND':
                $option = 'FIND IN SET';
                break;
            default:
                $option = '=';
        }

        return $option;
    }

    /**
     * 检测操作逻辑
     *
     * @param string $logic  条件逻辑
     */
    private function _checkWhereLogic(string $logic):string
    {
        $logic = strtoupper($logic);
        if (! in_array($logic, $this->whereLogic)) {
            $logic = $this->whereLogic[0];
        }

        return $logic;
    }
}
